﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using System;
using System.Linq;
using Utility.Text;

namespace Utility.CodeBuilder
{
	public enum AccessModifier
	{
		None,

		Public,
		Protected,
		Internal,
		ProtectedInternal,
		Private,
		PrivateProtected,
	}

	public enum InstanceModifier
	{
		None,

		Static,
		Abstract,
		Sealed,
	}

	public class CSharpCodeBuilder : TextBuilder
	{
		public CSharpCodeBuilder ()
		{
			Indenter = "\t";
		}

		public void UsingNameSpace (string namespaceName)
		{
			AppendLine ($"using {namespaceName};");
		}

		public TextScope NameSpace (string namespaceName)
		{
			AppendLine ($"namespace {namespaceName}");
			var scope = new CurlyBraceScope (this);
			scope.Start ();
			return scope;
		}

		#region class
		public TextScope Class (AccessModifier accessModifier, InstanceModifier instanceModifier, bool isPartial, string className, string baseClassName, string[] interfaceNames, string[] constrains)
		{
			//modifiers
			string s_access = GetString (accessModifier);
			string s_instance = GetString (instanceModifier);
			string s_partial = GetString_Partial (isPartial);

			//inherit
			string s_inherit = "";
			//base
			s_inherit += string.IsNullOrWhiteSpace (baseClassName) ? "" : $" {baseClassName},";
			//interface
			if (interfaceNames != null)
			{
				s_inherit += string.Concat (interfaceNames.Where (s => !string.IsNullOrWhiteSpace (s)).Select (s => $" {s},"));
			}

			//build string
			string str = $"{s_access}{s_instance}{s_partial}class {className}{s_inherit}".TrimEnd (' ', ',');
			AppendLine (str);
			if (constrains != null)
			{
				IndentLevel++;
				foreach (var c in constrains)
				{
					AppendLine (c);
				}
				IndentLevel--;
			}
			var scope = new CurlyBraceScope (this);
			scope.Start ();
			return scope;
		}

		public TextScope Class (AccessModifier accessModifier, InstanceModifier instanceModifier, string className)
		{
			return Class (accessModifier, instanceModifier, false, className, null, null, null);
		}
		#endregion
		#region const
		public void Const (AccessModifier accessModifier, string type, string name, string value)
		{
			string s_access = GetString (accessModifier);
			AppendLine ($"{s_access}const {type} {name} = {value};");
		}
		#endregion
		#region enum
		public void Enum (AccessModifier accessModifier, string name, string[] enumFields)
		{
			string s_access = GetString (accessModifier);
			AppendLine ($"{s_access}enum {name}");
			using (new CurlyBraceScope (this).Start ())
			{
				if (enumFields != null)
				{
					foreach (var s in enumFields)
					{
						if (!string.IsNullOrWhiteSpace (s))
						{
							AppendLine ($"{s},");
						}
					}
				}
			}
		}

		public void EnumFlags (AccessModifier accessModifier, string name, string[] enumFields, bool includeNone = true, bool includeAll = true)
		{
			if (enumFields != null && enumFields.Length > 32)
			{
				throw new Exception ("Flags enum cannot have more then 32 items");
			}

			string s_access = GetString (accessModifier);
			AppendLine ($"[System.Flags]");
			AppendLine ($"{s_access}enum {name}");
			using (new CurlyBraceScope (this).Start ())
			{
				if (enumFields != null)
				{
					for (int i = 0; i < enumFields.Length; i++)
					{
						var s = enumFields[i];
						if (!string.IsNullOrWhiteSpace (s))
						{
							AppendLine ($"{s} = 1 << {i},");
						}
					}
				}

				if (includeNone)
				{
					AppendLine ($"None = 0,");
				}
				if (includeAll)
				{
					AppendLine ($"All = ~0,");
				}
			}
		}
		#endregion

		#region utility
		string GetString (AccessModifier accessModifier)
		{
			switch (accessModifier)
			{
				case AccessModifier.Public:
					return "public ";
				case AccessModifier.Protected:
					return "protected ";
				case AccessModifier.Internal:
					return "internal ";
				case AccessModifier.ProtectedInternal:
					return "protected internal ";
				case AccessModifier.Private:
					return "private ";
				case AccessModifier.PrivateProtected:
					return "private protected";
				default:
					return "";
			}
		}

		string GetString (InstanceModifier instanceModifier)
		{
			switch (instanceModifier)
			{
				case InstanceModifier.Static:
					return "static ";
				case InstanceModifier.Abstract:
					return "abstract ";
				case InstanceModifier.Sealed:
					return "sealed ";
				default:
					return "";
			}
		}

		string GetString_Partial (bool isPartial)
		{
			return isPartial ? "partial " : "";
		}

		//ClassName<T1, T2>
		public string GetString_ClassName (string className, string[] genericNames)
		{
			if (genericNames == null)
			{
				return className;
			}
			className += "<";
			className += string.Concat (genericNames.Where (s => !string.IsNullOrWhiteSpace (s)).Select (s => $"{s}, "));
			className.TrimEnd (',', ' ');
			className += ">";
			return className;
		}
		#endregion

	}


}